package com.peas.platform.common.core.component;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.peas.platform.common.core.aop.BaseAopExecution;
import com.peas.platform.common.core.configuration.AlphaAopBean;
import com.peas.platform.common.core.configuration.RequestContext;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.constant.Constants;
import com.peas.platform.common.core.dto.ResponseDto;
import com.peas.platform.common.core.enums.Execution;
import com.peas.platform.common.core.feign.PeasModuleSysCoreFeign;
import com.peas.platform.common.core.utils.LogUtil;
import com.peas.platform.common.core.utils.RequestUtil;
import com.peas.platform.common.core.utils.SystemTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;

@Component
@Slf4j
public class AopExecutionAspect implements BaseAopExecution {

    @Value("${spring.profiles.active:local}")
    private String profiles;

    @Resource
    private ApplicationContext ac;
    @Resource
    private SystemTool systemTool;

    @Override
    public Execution exec() {
        return Execution.LOG;
    }

    @Override
    public void execution(final AlphaAopBean aab) {
        Class<?> clazz = aab.getClazz();
        Method method = aab.getMethod();
        boolean needAspect = true;
        Controller controllerOnClass = clazz.getAnnotation(Controller.class);
        RestController restControllerOnClass = clazz.getAnnotation(RestController.class);
        Component componentOnClass = clazz.getAnnotation(Component.class);
        FeignClient feignClientOnClass = clazz.getAnnotation(FeignClient.class);
        final AtomicReference<String> aspectType = new AtomicReference<>();
        Class<?> sup = clazz.getSuperclass();
        if (StrUtil.isBlank(aspectType.get()) && (controllerOnClass != null || restControllerOnClass != null)) {
            aspectType.set("Controller");
        }
        if (StrUtil.isBlank(aspectType.get()) && sup != null) {
            switch (sup.getName()) {
                case Constants.BASE_PACKAGE + ".framework.mybatisplus.service.BaseService":
                case Constants.BASE_PACKAGE + ".framework.mybatisplus.service.BaseServiceImpl":
                    aspectType.set("Service");
                    break;
                case Constants.BASE_PACKAGE + ".framework.mybatisplus.domain.BaseDomain":
                case Constants.BASE_PACKAGE + ".framework.mybatisplus.domain.BaseDomainImpl":
                    aspectType.set("Domain");
                    break;
                case Constants.BASE_PACKAGE + ".framework.mybatisplus.repository.BaseRepository":
                case "com.baomidou.mybatisplus.core.mapper.BaseMapper":
                    aspectType.set("Repository");
                    break;
            }
        }
        if (StrUtil.isBlank(aspectType.get()) && componentOnClass != null) {
            aspectType.set("Component");
        }
        if (feignClientOnClass != null) {
            aspectType.set(StrUtil.EMPTY);
        }
        if (StrUtil.isBlank(aspectType.get())) {
            log.debug("[needAspect]:StrUtil.isBlank(aspectType.get())");
            needAspect = false;
        }
        if (needAspect) {
            if (!StrUtil.equals("local", profiles)) {
                switch (this.exec()) {
                    case NONE:
                        break;
                    case LOG:
                        String content = JSONUtil.toJsonStr(aab);
                        LogUtil.log(aab.getClazz(), aab.getMethod().getName(), content, aab.getDuration(), "AOP Aspect");
                        break;
                    case SYNC:
                        aspect(aspectType.get(), clazz.getName(), method.getName(), aab.getDuration());
                        break;
                    case THREAD:
                        final RequestContext rc = RequestContextUtil.getRequestContext();
                        new Thread(() -> {
                            RequestUtil.initRequestId(rc);
                            aspect(aspectType.get(), clazz.getName(), method.getName(), aab.getDuration());
                        }).start();
                        break;
                }
            }
        }
    }

    private void aspect(String type, String clazz, String method, long duration) {
        if (systemTool.special()) {
            LogUtil.log(this.getClass(), "aspect", "type:" + type + ",clazz:" + clazz + ",method:" + method, duration);
            return;
        }
        JSONObject data = systemTool.buildJSONObject();
        data.set("type", type);
        data.set("clazz", clazz);
        data.set("method", method);
        data.set("aspectRequestId", RequestContextUtil.getCurrentRequestId());
        data.set("aspectTime", DateUtil.date());
        data.set("aspectDuration", duration);
        log.debug("addAspect:" + data);
        log.debug("call peasModuleSysMdmFeign.addAspect:" + JSONUtil.toJsonStr(data));
        PeasModuleSysCoreFeign peasModuleSysCoreFeign = ac.getBean(PeasModuleSysCoreFeign.class);
        ResponseDto<Integer> result = peasModuleSysCoreFeign.addAspect(data);
        log.debug("call peasModuleSysMdmFeign.addAspect, result:" + result);
    }

}
